/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "serpent_shrine.h"
#include "Player.h"

enum Talk
{
	SAY_AGGRO							= 0,
	SAY_SWITCH_TO_DEMON					= 1,
	SAY_INNER_DEMONS					= 2,
	SAY_DEMON_SLAY						= 3,
	SAY_NIGHTELF_SLAY					= 4,
	SAY_FINAL_FORM						= 5,
	SAY_DEATH							= 6
};

enum Spells
{
	SPELL_DUAL_WIELD					= 42459,
	SPELL_BANISH						= 37546,
	SPELL_TAUNT							= 37548,
	SPELL_BERSERK						= 26662,
	SPELL_WHIRLWIND						= 37640,
	SPELL_SUMMON_SHADOW_OF_LEOTHERAS	= 37781,

	// Demon Form
	SPELL_CHAOS_BLAST					= 37674,
	SPELL_CHAOS_BLAST_TRIGGER			= 37675,
	SPELL_INSIDIOUS_WHISPER				= 37676,
	SPELL_METAMORPHOSIS					= 37673,
	SPELL_SUMMON_INNER_DEMON			= 37735,
	SPELL_CONSUMING_MADNESS				= 37749,

	// Other
	SPELL_CLEAR_CONSUMING_MADNESS		= 37750,
	SPELL_SHADOW_BOLT					= 39309
};

enum Misc
{
	MAX_CHANNELERS						= 3,

	NPC_GREYHEART_SPELLBINDER			= 21806,
	NPC_SHADOW_OF_LEOTHERAS				= 21875,

	EVENT_SPELL_BERSERK					= 1,
	EVENT_HEALTH_CHECK					= 2,
	EVENT_SWITCH_TO_DEMON				= 3,
	EVENT_SPELL_WHIRLWIND				= 4,
	EVENT_KILL_TALK						= 5,
	EVENT_SWITCH_TO_ELF					= 6,
	EVENT_SPELL_INSIDIOUS_WHISPER		= 7,
	EVENT_SUMMON_DEMON					= 8,
	EVENT_RESTORE_FIGHT					= 9,

	EVENT_SPELL_SHADOW_BOLT				= 20
};

const Position channelersPos[MAX_CHANNELERS] = 
{
	{367.11f, -421.48f, 29.52f, 5.0f},
	{380.11f, -435.48f, 29.52f, 2.5f},
	{362.11f, -437.48f, 29.52f, 0.9f}
};

class boss_leotheras_the_blind : public CreatureScript
{
	public:
		boss_leotheras_the_blind() : CreatureScript("boss_leotheras_the_blind") { }

		CreatureAI* GetAI(Creature* creature) const
		{
			return GetInstanceAI<boss_leotheras_the_blindAI>(creature);
		}

		struct boss_leotheras_the_blindAI : public BossAI
		{
			boss_leotheras_the_blindAI(Creature* creature) : BossAI(creature, DATA_LEOTHERAS_THE_BLIND)
			{
			}

			void Reset()
			{
				BossAI::Reset();
				me->CastSpell(me, SPELL_CLEAR_CONSUMING_MADNESS, true);
				me->CastSpell(me, SPELL_DUAL_WIELD, true);
				me->SetStandState(UNIT_STAND_STATE_KNEEL);
				me->LoadEquipment(0, true);
				me->SetReactState(REACT_PASSIVE);
			}

			void InitializeAI()
			{
				BossAI::InitializeAI();	
				SummonChannelers();
			}

			void JustReachedHome()
			{
				BossAI::JustReachedHome();
				SummonChannelers();
			}

			void SummonChannelers()
			{
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_BANISH, false);
				me->CastSpell(me, SPELL_BANISH, true);
				me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_BANISH, true);

				summons.DespawnAll();
				for (uint8 i = 0; i < MAX_CHANNELERS; ++i)
					me->SummonCreature(NPC_GREYHEART_SPELLBINDER, channelersPos[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
			}

			void MoveInLineOfSight(Unit* who) {	}

			void JustSummoned(Creature* summon)
			{
				summons.Summon(summon);
			}

			void SummonedCreatureDies(Creature* summon, Unit*)
			{
				me->SetInCombatWithZone();
				summons.Despawn(summon);
				if (summon->GetEntry() == NPC_GREYHEART_SPELLBINDER)
					if (!summons.HasEntry(NPC_GREYHEART_SPELLBINDER))
					{
						me->RemoveAllAuras();
						me->LoadEquipment();
						me->SetReactState(REACT_AGGRESSIVE);
						me->SetStandState(UNIT_STAND_STATE_STAND);
						Talk(SAY_AGGRO);

						events.ScheduleEvent(EVENT_SPELL_BERSERK, 600000);
						events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000);
						events.ScheduleEvent(EVENT_SWITCH_TO_DEMON, 55000);
						events.ScheduleEvent(EVENT_SPELL_WHIRLWIND, 10000);
					}
			}

			void KilledUnit(Unit* victim)
			{
				if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
				{
					Talk(me->GetDisplayId() != me->GetNativeDisplayId() ? SAY_DEMON_SLAY : SAY_NIGHTELF_SLAY);
					events.ScheduleEvent(EVENT_KILL_TALK, 6000);
				}
			}

			void JustDied(Unit* killer)
			{
				me->CastSpell(me, SPELL_CLEAR_CONSUMING_MADNESS, true);
				Talk(SAY_DEATH);
				BossAI::JustDied(killer);
			}

			void EnterCombat(Unit* who)
			{
				BossAI::EnterCombat(who);
				me->SetStandState(UNIT_STAND_STATE_KNEEL);
			}

			void AttackStart(Unit* who)
			{
				if (who && me->Attack(who, true))
					me->GetMotionMaster()->MoveChase(who, me->GetDisplayId() == me->GetNativeDisplayId() ? 0.0f : 25.0f);
			}

			void UpdateAI(uint32 diff)
			{
				if (!UpdateVictim())
					return;

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_SPELL_BERSERK:
						me->CastSpell(me, SPELL_BERSERK, true);
						break;
					case EVENT_HEALTH_CHECK:
						if (me->HealthBelowPct(15))
						{
							if (me->GetDisplayId() != me->GetNativeDisplayId())
							{
								DoResetThreat();
								me->LoadEquipment();
								me->RemoveAurasDueToSpell(SPELL_METAMORPHOSIS);
								events.ScheduleEvent(EVENT_SPELL_WHIRLWIND, 10000);
							}
							events.CancelEvent(EVENT_SWITCH_TO_DEMON);
							events.CancelEvent(EVENT_SPELL_INSIDIOUS_WHISPER);
							events.DelayEvents(10000);
							events.ScheduleEvent(EVENT_SUMMON_DEMON, 4000);
							events.ScheduleEvent(EVENT_RESTORE_FIGHT, 6000);
							me->SetStandState(UNIT_STAND_STATE_KNEEL);
							me->SetReactState(REACT_PASSIVE);
							me->GetMotionMaster()->Clear();
							me->StopMoving();
							Talk(SAY_FINAL_FORM);
							break;
						}
						events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000);
						break;
					case EVENT_SWITCH_TO_DEMON:
						DoResetThreat();
						Talk(SAY_SWITCH_TO_DEMON);
						me->LoadEquipment(0, true);
						me->GetMotionMaster()->MoveChase(me->GetVictim(), 25.0f);
						me->CastSpell(me, SPELL_METAMORPHOSIS, true);

						events.CancelEvent(EVENT_SPELL_WHIRLWIND);
						events.ScheduleEvent(EVENT_SPELL_INSIDIOUS_WHISPER, 25000);
						events.ScheduleEvent(EVENT_SWITCH_TO_ELF, 60000);
						break;
					case EVENT_SWITCH_TO_ELF:
						DoResetThreat();
						me->LoadEquipment();
						me->GetMotionMaster()->MoveChase(me->GetVictim(), 0.0f);
						me->RemoveAurasDueToSpell(SPELL_METAMORPHOSIS);
						events.ScheduleEvent(EVENT_SWITCH_TO_DEMON, 55000);
						events.ScheduleEvent(EVENT_SPELL_WHIRLWIND, 10000);
						break;
					case EVENT_SPELL_WHIRLWIND:
						me->CastSpell(me, SPELL_WHIRLWIND, false);
						events.ScheduleEvent(EVENT_SPELL_WHIRLWIND, 27000);
						break;
					case EVENT_SPELL_INSIDIOUS_WHISPER:
						Talk(SAY_INNER_DEMONS);
						me->CastCustomSpell(SPELL_INSIDIOUS_WHISPER, SPELLVALUE_MAX_TARGETS, 5, me, false);
						break;
					case EVENT_SUMMON_DEMON:
						me->CastSpell(me, SPELL_SUMMON_SHADOW_OF_LEOTHERAS, true);
						break;
					case EVENT_RESTORE_FIGHT:
						me->SetStandState(UNIT_STAND_STATE_STAND);
						me->SetReactState(REACT_AGGRESSIVE);
						me->GetMotionMaster()->MoveChase(me->GetVictim());
						break;
				}

				if (me->GetDisplayId() == me->GetNativeDisplayId())
					DoMeleeAttackIfReady();
				else if (me->isAttackReady(BASE_ATTACK))
				{
					me->CastSpell(me->GetVictim(), SPELL_CHAOS_BLAST, false);
					me->setAttackTimer(BASE_ATTACK, 2000);
				}
			}
		};
};

class npc_inner_demon : public CreatureScript
{
	public:
		npc_inner_demon() : CreatureScript("npc_inner_demon") { }

		CreatureAI* GetAI(Creature* creature) const
		{
			return new npc_inner_demonAI(creature);
		}

		struct npc_inner_demonAI : public ScriptedAI
		{
			npc_inner_demonAI(Creature* creature) : ScriptedAI(creature)
			{
			}

			uint64 ownerGUID;
			EventMap events;

			void EnterEvadeMode()
			{
				me->DespawnOrUnsummon(1);
			}

			void IsSummonedBy(Unit* summoner)
			{
				if (!summoner)
					return;

				ownerGUID = summoner->GetGUID();
				events.Reset();
				events.ScheduleEvent(EVENT_SPELL_SHADOW_BOLT, 4000);
			}

			void JustDied(Unit* /*killer*/)
			{
				if (Unit* unit = ObjectAccessor::GetUnit(*me, ownerGUID))
					unit->RemoveAurasDueToSpell(SPELL_INSIDIOUS_WHISPER);
			}

			void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask)
			{
				if (!who || who->GetGUID() != ownerGUID)
					damage = 0;
			}

			bool CanAIAttack(const Unit* who) const
			{
				return who->GetGUID() == ownerGUID;
			}

			void UpdateAI(uint32 diff)
			{
				if (!UpdateVictim())
					return;

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_SPELL_SHADOW_BOLT:
						me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false);
						events.ScheduleEvent(EVENT_SPELL_SHADOW_BOLT, 6000);
						break;
				}

				DoMeleeAttackIfReady();
			}
		};
};

class spell_leotheras_whirlwind : public SpellScriptLoader
{
	public:
		spell_leotheras_whirlwind() : SpellScriptLoader("spell_leotheras_whirlwind") { }

		class spell_leotheras_whirlwind_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_leotheras_whirlwind_SpellScript);

			void HandleScriptEffect(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				GetCaster()->getThreatManager().resetAllAggro();

				if (roll_chance_i(33))
					if (Unit* target = GetCaster()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f, true))
						target->CastSpell(GetCaster(), SPELL_TAUNT, true);

			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_leotheras_whirlwind_SpellScript::HandleScriptEffect, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_leotheras_whirlwind_SpellScript();
		}
};

class spell_leotheras_chaos_blast : public SpellScriptLoader
{
	public:
		spell_leotheras_chaos_blast() : SpellScriptLoader("spell_leotheras_chaos_blast") { }

		class spell_leotheras_chaos_blast_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_leotheras_chaos_blast_SpellScript);

			void HandleDummy(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				if (Unit* target = GetHitUnit())
					GetCaster()->CastSpell(target, SPELL_CHAOS_BLAST_TRIGGER, true);
			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_leotheras_chaos_blast_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_leotheras_chaos_blast_SpellScript();
		}
};

class spell_leotheras_insidious_whisper : public SpellScriptLoader
{
    public:
        spell_leotheras_insidious_whisper() : SpellScriptLoader("spell_leotheras_insidious_whisper") { }

        class spell_leotheras_insidious_whisper_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_leotheras_insidious_whisper_SpellScript);

            void FilterTargets(std::list<WorldObject*>& unitList)
            {
				if (Unit* victim = GetCaster()->GetVictim())
					unitList.remove_if(Trinity::ObjectGUIDCheck(victim->GetGUID(), true));
            }

            void Register()
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_leotheras_insidious_whisper_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_leotheras_insidious_whisper_SpellScript();
        }

		class spell_leotheras_insidious_whisper_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_leotheras_insidious_whisper_AuraScript)

			void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
			{
				GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SUMMON_INNER_DEMON, true);
			}

			void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
			{
				if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT)
					if (InstanceScript* instance = GetUnitOwner()->GetInstanceScript())
						if (Creature* leotheras = ObjectAccessor::GetCreature(*GetUnitOwner(), instance->GetData64(NPC_LEOTHERAS_THE_BLIND)))
							leotheras->CastSpell(GetUnitOwner(), SPELL_CONSUMING_MADNESS, true);
			}

			void Register()
			{
				AfterEffectApply += AuraEffectApplyFn(spell_leotheras_insidious_whisper_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
				AfterEffectRemove += AuraEffectRemoveFn(spell_leotheras_insidious_whisper_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_leotheras_insidious_whisper_AuraScript();
		}
};

class spell_leotheras_demon_link : public SpellScriptLoader
{
    public:
        spell_leotheras_demon_link() : SpellScriptLoader("spell_leotheras_demon_link") { }

        class spell_leotheras_demon_link_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_leotheras_demon_link_AuraScript);

            void OnPeriodic(AuraEffect const* aurEff)
            {
				PreventDefaultAction();
				if (Unit* victim = GetUnitOwner()->GetVictim())
					GetUnitOwner()->CastSpell(victim, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true);
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_leotheras_demon_link_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_leotheras_demon_link_AuraScript();
        }
};

class spell_leotheras_clear_consuming_madness : public SpellScriptLoader
{
	public:
		spell_leotheras_clear_consuming_madness() : SpellScriptLoader("spell_leotheras_clear_consuming_madness") { }

		class spell_leotheras_clear_consuming_madness_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_leotheras_clear_consuming_madness_SpellScript);

			void HandleScriptEffect(SpellEffIndex effIndex)
			{
				PreventHitDefaultEffect(effIndex);
				if (Unit* target = GetHitUnit())
					Unit::Kill(GetCaster(), target);
			}

			void Register()
			{
				OnEffectHitTarget += SpellEffectFn(spell_leotheras_clear_consuming_madness_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_leotheras_clear_consuming_madness_SpellScript();
		}
};

void AddSC_boss_leotheras_the_blind()
{
    new boss_leotheras_the_blind();
    new npc_inner_demon();
	new spell_leotheras_whirlwind();
	new spell_leotheras_chaos_blast();
	new spell_leotheras_insidious_whisper();
	new spell_leotheras_demon_link();
	new spell_leotheras_clear_consuming_madness();
}
